<script setup lang="ts">
const el = useTemplateRef<HTMLCanvasElement>('el')
const ctx = computed(() => el.value!.getContext('2d')!)
const WIDTH = 600
const HEIGHT = 600
interface Point {
  x: number
  y: number
}

interface Branch {
  start: Point
  length: number
  angle: number
  currentLength: number
}

function init() {
  ctx.value.strokeStyle = '#000'
  ctx.value.font = '1px serif'
  const branch: Branch = {
    start: { x: WIDTH / 2, y: HEIGHT },
    length: 10,
    angle: -Math.PI / 2,
    currentLength: 0,
  }
  step(branch)
}
async function step(b: Branch, depth = 0) {
  const end = getEndPoint(b, b.length)
  const result = await drawLine(b)
  if (result) {
    if (depth < (15 + Math.random() * 1)) {
      if (depth < 3 || Math.random() < 0.6) {
        const leftBranch: Branch = {
          start: end,
          length: b.length + (6 * Math.random() - 3),
          // angle: (b.angle + 0.3 * Math.random()) > 0 || (b.angle + 0.3 * Math.random()) < -Math.PI ? (b.angle + 0.3 * Math.random()) : (b.angle - 0.3 * Math.random()),
          angle: (b.angle - 0.4 * Math.random()),
          currentLength: 0,
        }
        step(leftBranch, depth + 1)
      }
    }
    if (depth < (15 + Math.random() * 1)) {
      if (depth < 3 || Math.random() < 0.6) {
        const rightBranch: Branch = {
          start: end,
          length: b.length + (6 * Math.random() - 3),
          // angle: (b.angle + 0.3 * Math.random()) > 0 || (b.angle + 0.3 * Math.random()) < -Math.PI ? (b.angle - 0.3 * Math.random()) : (b.angle + 0.3 * Math.random()),
          angle: (b.angle + 0.3 * Math.random()),
          currentLength: 0,
        }
        step(rightBranch, depth + 1)
      }
    }
  }
}
async function lineTo(b1: Branch, b2: Branch) {
  return await new Promise((resolve) => {
    const line1 = (b1: Branch, b2: Branch) => {
      const p1 = b1.start
      const lineSpeed = 0.2
      b1.currentLength += lineSpeed
      if (b1.currentLength >= b1.length) {
        b1.currentLength = b1.length
      }
      const currentEnd = getEndPoint(b1, lineSpeed)
      ctx.value.beginPath()
      ctx.value.moveTo(p1.x, p1.y)
      ctx.value.lineTo(currentEnd.x, currentEnd.y)
      ctx.value.stroke()
      const pNext = {
        ...b1,
        start: {
          x: currentEnd.x,
          y: currentEnd.y,
        },

      }
      if (b1.currentLength >= b1.length) {
        resolve(true)
        return
      }
      requestAnimationFrame(() => {
        line1(pNext, b2)
      })
    }
    line1(b1, b2)
  })
}

function getEndPoint(b: Branch, length: number) {
  return {
    x: b.start.x + length * Math.cos(b.angle),
    y: b.start.y + length * Math.sin(b.angle),
  }
}
function getEndBranch(b: Branch): Branch {
  const end = getEndPoint(b, b.length)
  return {
    start: {
      ...end,
    },
    angle: b.angle,
    length: b.length,
    currentLength: 0,
  }
}
async function drawLine(b: Branch) {
  const result = await lineTo(b, getEndBranch(b))
  return result
}
onMounted(() => {
  init()
})
</script>

<template>
  <div>
    <canvas ref="el" width="600" height="600"/>
  </div>
</template>
